
#include "BillboardGeom.h"
#include "Entity.h"
#include "Engine.h"
#include "Camera.h"

vector<BillboardGeom*> BillboardGeom::bbgeoms;

#define USE_METHOD_1

//
// Constructor
//

BillboardGeom::BillboardGeom(Entity *_parent, bool _isFixedPoint) : Geometry(_parent)
{
	isFixedPoint = _isFixedPoint;
	isBillboardGeom = true;

	// Add to the global list
	bbgeoms.push_back(this);

	parent->SetGravityModifier(0.0f);
}

//
// Clear
//

void BillboardGeom::Clear()
{
	// Clears mesh
	mesh.Clear();

	// Clears billboards
	billBoards.clear();
}

//
// AddBillboard
//

void BillboardGeom::AddBillboard(float size, const Real3 &point)
{
	billBoards.push_back(Billboard(point, size));
}

//
// GenerateBillboards
//

void BillboardGeom::GenerateBillboards()
{
	if(updateBillboards)
	{
		if(isFixedPoint)
		{
			#ifdef USE_METHOD_1
			//Real3 normal = -1.0f * engine.camera->Direction();
			Real3 right  =  0.5f * engine.camera->Right();
			#endif
			Real3 up     =  0.5f * engine.camera->Up();

			// Remove old data
			mesh.Clear();

			// Generate new data
			for(uint i = 0; i < billBoards.size(); ++i)
			{
				Real3 &p = billBoards[i].point;
				Real3  u = billBoards[i].size * up;
				#ifdef USE_METHOD_1
				Real3  r = billBoards[i].size * right;
				#else
				Real3  normal = engine.camera->Position() - billBoards[i].point;
				normal.Normalize();
				Real3  r = 0.5f * billBoards[i].size * Cross(2.0f * up, normal);
				#endif

				ushort v0 = mesh.AddVertex(p - r + u, Vector3(0.0f, 1.0f, 0.0f), Real2(0.0f, 0.0f));
				ushort v1 = mesh.AddVertex(p + r + u, Vector3(0.0f, 1.0f, 0.0f), Real2(1.0f, 0.0f));
				ushort v2 = mesh.AddVertex(p + r - u, Vector3(0.0f, 1.0f, 0.0f), Real2(1.0f, 1.0f));
				ushort v3 = mesh.AddVertex(p - r - u, Vector3(0.0f, 1.0f, 0.0f), Real2(0.0f, 1.0f));

				mesh.AddQuad(v0, v1, v2, v3);
			}
		}
		else
		{
			Real3 normal = -1.0f * engine.camera->Direction();
			Real3 right  =  0.5f * engine.camera->Right();
			Real3 up     =  0.5f * engine.camera->Up();

			// Remove old data
			mesh.Clear();

			// Generate new data
			for(uint i = 0; i < billBoards.size(); ++i)
			{
				Real3 p = engine.camera->Position() + 0.15f * engine.camera->Direction();
				Real3 r = billBoards[i].size * right;
				Real3 u = billBoards[i].size * up;

				ushort v0 = mesh.AddVertex(p - r + u, Vector3(0.0f, 1.0f, 0.0f), Real2(0.0f, 0.0f));
				ushort v1 = mesh.AddVertex(p + r + u, Vector3(0.0f, 1.0f, 0.0f), Real2(1.0f, 0.0f));
				ushort v2 = mesh.AddVertex(p + r - u, Vector3(0.0f, 1.0f, 0.0f), Real2(1.0f, 1.0f));
				ushort v3 = mesh.AddVertex(p - r - u, Vector3(0.0f, 1.0f, 0.0f), Real2(0.0f, 1.0f));

				mesh.AddQuad(v0, v1, v2, v3);
			}
		}
	}
}

//
// _Render
//

void BillboardGeom::_Render(uchar renderFlag)
{
	#ifdef USE_MATERIALS
	parent->LoadMaterial();
	#endif
	engine.LoadMatrices(parent->WorldMatrix());

	// Loads the texture (if exists)
	#ifdef USE_TEX
	if(texture) texture->Load();
	else Texture::Clear();
	#endif

	GenerateBillboards();

	mesh.CreateBuffers();
	mesh.LoadBuffers(renderFlag);
}

//
// RenderBBGeoms
//

void BillboardGeom::RenderBBGeoms(uchar renderFlag)
{
	for(uint i = 0; i < bbgeoms.size(); ++i)
		bbgeoms[i]->_Render(renderFlag);
}